////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek, 2010.
// -------------------------------------------------------------------------
//  File name:   CryGameSpyDLCStore.h
//  Version:     v1.00
//  Created:     02/09/2010 by Paul Mikell.
//  Description: CCryGameSpyDLCStore class definition
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////


#ifndef __CRYGAMESPYDLCSTORE_H__

#define __CRYGAMESPYDLCSTORE_H__

#if _MSC_VER > 1000
#pragma once
#endif


#include "Lobby/GameSpy/CryGameSpyLobby.h"


#if USE_CRYLOBBY_GAMESPY


#include "Lobby/CryDLCStore.h"
#include "Gamespy2.11.00/Source/Direct2Game/Direct2Game.h"


struct SCryGameSpyDLCCategoryID: public SCryDLCCategoryID
{
	virtual bool operator==( const SCryDLCCategoryID& other ) { return !wcscmp( name.c_str(), static_cast< const SCryGameSpyDLCCategoryID& >( other ).name.c_str() ); };

	wstring				name;
};


struct SCryGameSpyDLCItemID: public SCryDLCItemID
{
	SCryGameSpyDLCItemID(): itemID( 0 ) {};
	SCryGameSpyDLCItemID( D2GItemId id ): itemID( id ) {};
	virtual bool operator==( const SCryDLCItemID& other ) { return itemID == static_cast< const SCryGameSpyDLCItemID& >( other ).itemID; };

	D2GItemId			itemID;
};


struct SCryGameSpyDLCImageID: public SCryDLCImageID
{
	virtual bool operator==( const SCryDLCImageID& other ) { return !wcscmp( name.c_str(), static_cast< const SCryGameSpyDLCImageID& >( other ).name.c_str() ); };

	wstring				name;
};


struct SCryGameSpyDLCDownloadableID: public SCryDLCDownloadableID
{
	SCryGameSpyDLCDownloadableID(): fileID( 0 ) {};
	SCryGameSpyDLCDownloadableID( D2GFileId id ): fileID( id ) {};
	virtual bool operator==( const SCryDLCDownloadableID& other ) { return fileID == static_cast< const SCryGameSpyDLCDownloadableID& >( other ).fileID; };

	D2GFileId			fileID;
};


struct SCryGameSpyDLCLicense: public SCryDLCLicense
{
	virtual bool operator==( const SCryDLCLicense& other ) { return !( wcscmp( name.c_str(), static_cast< const SCryGameSpyDLCLicense& >( other ).name.c_str() ) || wcscmp( key.c_str(), static_cast< const SCryGameSpyDLCLicense& >( other ).key.c_str() ) ); };

	wstring				name;
	wstring				key;
};


struct SCryGameSpyDLCPaymentMethodID: public SCryDLCPaymentMethodID
{
	virtual bool operator==( const SCryDLCPaymentMethodID& other ) { return accountID == static_cast< const SCryGameSpyDLCPaymentMethodID& >( other ).accountID; };

	gsi_u32				accountID;
};


class CCryGameSpyDLCStore: public CCryDLCStore, public ICryGameSpyLobbyComponent
{
public:

	CCryGameSpyDLCStore( CCryLobby* pLobby, CCryLobbyService* pService );

	virtual ECryLobbyError Initialise();
	virtual ECryLobbyError Terminate();
	virtual void Tick( CTimeValue tv );

	// ICryDLCStore
	virtual ECryLobbyError CheckAvailability( CryUserID userID, CryLobbyTaskID* pTaskID, CryDLCCheckAvailabilityCallback cb, void* pCbArg );
	virtual ECryLobbyError CatalogListItems( CryLobbyTaskID* pTaskID, CryDLCListItemsCallback cb, void* pCbArg );
	virtual ECryLobbyError CatalogListCategories( CryLobbyTaskID* pTaskID, CryDLCListCategoriesCallback cb, void* pCbArg );
	virtual ECryLobbyError CategoryGetInfo( CryDLCCategoryID categoryID, CryLobbyTaskID* pTaskID, CryDLCListCategoriesCallback cb, void* pCbArg );
	virtual ECryLobbyError CategoryListItems( CryDLCCategoryID categoryID, CryLobbyTaskID* pTaskID, CryDLCListItemsCallback cb, void* pCbArg );
	virtual ECryLobbyError ItemGetInfo( CryDLCItemID itemID, CryLobbyTaskID* pTaskID, CryDLCListItemsCallback cb, void* pCbArg );
	virtual ECryLobbyError DownloadableIsDownloaded( CryDLCDownloadableID downloadableID, const char* pDstPath, CryLobbyTaskID* pTaskID, CryDLCDownloadableIsDownloadedCallback cb, void* pCbArg );
	virtual ECryLobbyError UserGetPurchaseHistory( CryUserID userID, CryLobbyTaskID* pTaskID, CryDLCOrderFinalizeCallback cb, void* pCbArg );
	virtual ECryLobbyError UserStartOrder( CryUserID userID, CryDLCOrderHandle* pOrderHandle, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg );
	virtual ECryLobbyError UserGetDownloadableAccess( CryUserID userID, CryDLCDownloadableID downloadableID, CryLobbyTaskID* pTaskID, CryDLCUserGetDownloadableAccessCallback cb, void* pCbArg );
	virtual ECryLobbyError UserDownloadItem( CryUserID userID, CryDLCDownloadableID downloadableID, CryLobbyTaskID* pTaskID, CryDLCUserDownloadItemCallback cb, void* pCbArg );
	virtual ECryLobbyError UserCancelActiveDownload( CryUserID userID );
	virtual ECryLobbyError UserRedownloadAllMissingItems( CryUserID userID, CryLobbyTaskID* pTaskID, CryDLCUserDownloadItemCallback cb, void* pCbArg );
	virtual ECryLobbyError UserListPaymentMethods( CryUserID userID, CryLobbyTaskID* pTaskID, CryDLCListPaymentMethodsCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderFinalize( CryDLCOrderHandle orderHandle, bool freeOnCompletion, CryLobbyTaskID* pTaskID, CryDLCOrderFinalizeCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderAddItem( CryDLCOrderHandle orderHandle, CryDLCItemID itemID, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderRemoveItem( CryDLCOrderHandle orderHandle, CryDLCItemID itemID, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderAddItemQuantity( CryDLCOrderHandle orderHandle, CryDLCItemID itemID, uint32 quantity, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderRemoveItemQuantity( CryDLCOrderHandle orderHandle, CryDLCItemID itemID, uint32 quantity, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderListItems( CryDLCOrderHandle orderHandle, CryLobbyTaskID* pTaskID, CryDLCOrderListItemsCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderGetInfo( CryDLCOrderHandle orderHandle, CryLobbyTaskID* pTaskID, CryDLCOrderGetInfoCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderGetItemInfo( CryDLCOrderHandle orderHandle, CryDLCItemID itemID, CryLobbyTaskID* pTaskID, CryDLCOrderGetItemInfoCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderSetPaymentMethod( CryDLCOrderHandle orderHandle, CryDLCPaymentMethodID paymentMethodID, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderCancel( CryDLCOrderHandle orderHandle, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg );
	virtual ECryLobbyError OrderFree( CryDLCOrderHandle orderHandle, CryLobbyTaskID* pTaskID, CryDLCCallback cb, void* pCbArg );
	virtual ECryLobbyError GetImageNameFromImageID( CryDLCImageID imageID, CryStringT< wchar_t >* pImageName );
	virtual ECryLobbyError GetRealDLCItemID( CryDLCItemID itemID, uint32* idOut );
	// ~ICryDLCStore

	// ICryGameSpyLobbyComponent
	virtual void OnGameSpyObjectAvailable( EGameSpyObjectBit object );
	virtual void OnGameSpyObjectUnavailable( EGameSpyObjectBit object );
	virtual void OnGameSpyObjectCantMakeAvailable( EGameSpyObjectBit object );
	// ~ICryGameSpyLobbyComponent

protected:

	enum EGameSpyTask
	{
		eGST_RefreshCatalog = eT_DLCStorePlatformSpecificTask,
		eGST_RevertItemQuantity,
		eGST_GetOrderTotal
	};

	enum ETotalState
	{
		eTS_Invalid,
		eTS_Updating,
		eTS_UpdateFailed,
		eTS_Valid
	};

	struct SOrderLink;
	struct SOrder;
	typedef _smart_ptr< SOrderLink >	SOrderLinkPtr;

	struct SOrderLink: public CMultiThreadRefCount
	{
		SOrderLink( SOrder* pOrder, CCryGameSpyDLCStore* pDLCStore )
		:	m_pOrder( pOrder ),
			m_pDLCStore( pDLCStore ),
			m_pVector( NULL )
		{
		};

		void AddToVector( std::vector< SOrderLinkPtr >& pVector )
		{
			m_pVector = &pVector;
			m_pVector->push_back( this );
		};

		void RemoveFromVector()
		{
			std::vector< SOrderLinkPtr >*						pVector = m_pVector;
			std::vector< SOrderLinkPtr >::iterator	ppThis = std::find( pVector->begin(), pVector->end(), SOrderLinkPtr( this ) );

			if ( ppThis != pVector->end() )
			{
				*ppThis = ( *pVector )[ pVector->size() - 1 ];
				pVector->pop_back();
			}
		};

		SOrder*												m_pOrder;
		_smart_ptr< CCryGameSpyDLCStore >	m_pDLCStore;
		std::vector< SOrderLinkPtr >*	m_pVector;

	private:

		SOrderLink();
	};

	struct STaskLink;
	struct STask;
	typedef _smart_ptr< STaskLink >	STaskLinkPtr;

	struct STaskLink: public CMultiThreadRefCount
	{
		STaskLink( STask* pTask, CCryGameSpyDLCStore* pDLCStore, CCryLobby* pLobby )
		:	m_pTask( pTask ),
			m_pDLCStore( pDLCStore ),
			m_pLobby( pLobby ),
			m_pVector( NULL )
		{
		};

		void AddToVector( std::vector< STaskLinkPtr >& pVector )
		{
			m_pVector = &pVector;
			m_pVector->push_back( this );
		};

		void RemoveFromVector()
		{
			std::vector< STaskLinkPtr >*					pVector = m_pVector;
			std::vector< STaskLinkPtr >::iterator	ppThis = std::find( pVector->begin(), pVector->end(), STaskLinkPtr( this ) );

			if ( ppThis != pVector->end() )
			{
				*ppThis = ( *pVector )[ pVector->size() - 1 ];
				pVector->pop_back();
			}
		};

		CryFixedStringT< _MAX_PATH >	m_downloadPath;
		STask*												m_pTask;
		_smart_ptr< CCryGameSpyDLCStore >	m_pDLCStore;
		CCryLobby*										m_pLobby;
		std::vector< STaskLinkPtr >*	m_pVector;

	private:

		STaskLink();
	};

	struct STask: public CCryDLCStore::STask
	{
		STaskLinkPtr						pLink;
		uint8										oldGameSpyObjects;	// Required to be ready when task starts; starting task when they're not ready is an error. 
		uint8										newGameSpyObjects;	// Will be used; not an error to start task without them. Lobby service is notified that they're needed, possibly starting login.																												 
		uint8										haveGameSpyObjects;	// Objects ready at present.
		bool										started;
	};

	struct SOrder: public CCryDLCStore::SOrder
	{
		SOrderLinkPtr						pLink;
		CTimeValue							lastPollTime;
		D2GOrderTotal*					pTotal;
		D2GOrderPurchase*				pPurchase;
		ETotalState							totalState;
		bool										polling;
	};

#pragma warning( push )
#pragma warning( disable : 4200 )
	struct SCategoryGetInfoParam
	{
		int32										index;
		wchar_t									name[];
	};
#pragma warning( pop )

	struct SItemGetInfoParam
	{
		int32										index;
		D2GItemId								itemID;
	};

	struct SUserGetPurchaseHistoryParam
	{
		GPProfile								profileID;
	};

	enum EPurchaseHistoryItemType
	{
		ePHIT_Purchasable,
		ePHIT_Downloadable,
		ePHIT_License
	};

	struct SPurchaseHistoryPurchasable
	{
		D2GItemId											itemID;
	};

	struct SPurchaseHistoryDownloadable
	{
		D2GFileId											fileID;
	};

	struct SPurchaseHistoryLicense
	{
		wchar_t*											name;
		wchar_t*											key;
	};

	union UPurchaseHistoryItem
	{
		SPurchaseHistoryPurchasable		purchasable;
		SPurchaseHistoryDownloadable	downloadable;
		SPurchaseHistoryLicense				license;
	};

	struct SPurchaseHistoryItem
	{
		EPurchaseHistoryItemType			type;
		UPurchaseHistoryItem					data;
	};

	struct SDispatchDownloadCompleteParam
	{
		CryDLCUserDownloadItemCallback	cb;
		CryLobbyTaskID									lTaskID;
		ECryLobbyError									error;
		CryDLCDownloadableID						id;
		bool														complete;
		string													path;
		void*														pCbArg;
	};

	void StartCheckAvailability( CryDLCStoreTaskID dlcsTaskID );
	void TickCheckAvailability( CryDLCStoreTaskID dlcsTaskID );

	void StartCatalogListItems( CryDLCStoreTaskID dlcsTaskID );
	void TickCatalogListItems( CryDLCStoreTaskID dlcsTaskID );
	void EndCatalogListItems( CryDLCStoreTaskID dlcsTaskID );

	void StartCatalogListCategories( CryDLCStoreTaskID dlcsTaskID );
	void TickCatalogListCategories( CryDLCStoreTaskID dlcsTaskID );
	void EndCatalogListCategories( CryDLCStoreTaskID dlcsTaskID );

	void StartCategoryGetInfo( CryDLCStoreTaskID dlcsTaskID );
	void TickCategoryGetInfo( CryDLCStoreTaskID dlcsTaskID );
	void EndCategoryGetInfo( CryDLCStoreTaskID dlcsTaskID );

	void StartCategoryListItems( CryDLCStoreTaskID dlcsTaskID );
	void TickCategoryListItems( CryDLCStoreTaskID dlcsTaskID );
	void EndCategoryListItems( CryDLCStoreTaskID dlcsTaskID );
	ECryLobbyError GetCategoryItems( CryDLCStoreTaskID dlcsTaskID, wchar_t* pName, uint32 i, uint32 count, uint32*& pItemIndex );

	void StartItemGetInfo( CryDLCStoreTaskID dlcsTaskID );
	void TickItemGetInfo( CryDLCStoreTaskID dlcsTaskID );
	void EndItemGetInfo( CryDLCStoreTaskID dlcsTaskID );

	void StartDownloadableIsDownloaded( CryDLCStoreTaskID dlcsTaskID );

	void StartUserGetPurchaseHistory( CryDLCStoreTaskID dlcsTaskID );
	void TickUserGetPurchaseHistory( CryDLCStoreTaskID dlcsTaskID );

	void StartUserStartOrder( CryDLCStoreTaskID dlcsTaskID );

	void StartUserGetDownloadableAccess( CryDLCStoreTaskID dlcsTaskID );
	void TickUserGetDownloadableAccess( CryDLCStoreTaskID dlcsTaskID );
	void EndUserGetDownloadableAccess( CryDLCStoreTaskID dlcsTaskID );

	void StartUserDownloadItem( CryDLCStoreTaskID dlcsTaskID );
	void TickUserDownloadItem( CryDLCStoreTaskID dlcsTaskID );
	void EndUserDownloadItem( CryDLCStoreTaskID dlcsTaskID );
	void DispatchDownloadResult( CryLobbyTaskID lTaskID, ECryLobbyError error, CryDLCDownloadableID downloadableID, bool complete, uint32 bytesReceived, uint32 bytesTotal, CryDLCUserDownloadItemCallback cb, void* pCbArg );

	void CancelActiveDownloadInternal();

	void StartUserRedownloadAllMissingItems( CryDLCStoreTaskID dlcsTaskID );
	void TickUserRedownloadAllMissingItems( CryDLCStoreTaskID dlcsTaskID );
	void EndUserRedownloadAllMissingItems( CryDLCStoreTaskID dlcsTaskID );

	void StartUserListPaymentMethods( CryDLCStoreTaskID dlcsTaskID );
	void TickUserListPaymentMethods( CryDLCStoreTaskID dlcsTaskID );
	void EndUserListPaymentMethods( CryDLCStoreTaskID dlcsTaskID );

	void StartOrderFinalize( CryDLCStoreTaskID dlcsTaskID );
	void TickOrderFinalize( CryDLCStoreTaskID dlcsTaskID );
	void ReportPurchaseHistory( CryDLCStoreTaskID dlcsTaskID );

	void StartOrderAdjustItemQuantity( CryDLCStoreTaskID dlcsTaskID );
	void TickOrderAdjustItemQuantity( CryDLCStoreTaskID dlcsTaskID );

	void StartOrderListItems( CryDLCStoreTaskID dlcsTaskID );
	void EndOrderListItems( CryDLCStoreTaskID dlcsTaskID );

	void StartOrderGetInfo( CryDLCStoreTaskID dlcsTaskID );
	void EndOrderGetInfo( CryDLCStoreTaskID dlcsTaskID );

	void StartOrderGetItemInfo( CryDLCStoreTaskID dlcsTaskID );
	void TickOrderGetItemInfo( CryDLCStoreTaskID dlcsTaskID );
	void EndOrderGetItemInfo( CryDLCStoreTaskID dlcsTaskID );

	void StartOrderSetPaymentMethod( CryDLCStoreTaskID dlcsTaskID );
	void TickOrderSetPaymentMethod( CryDLCStoreTaskID dlcsTaskID );

	void StartOrderCancel( CryDLCStoreTaskID dlcsTaskID );
	void TickOrderCancel( CryDLCStoreTaskID dlcsTaskID );
	void EndOrderCancel( CryDLCStoreTaskID dlcsTaskID );

	void StartOrderFree( CryDLCStoreTaskID dlcsTaskID );
	void TickOrderFree( CryDLCStoreTaskID dlcsTaskID );
	void EndOrderFree( CryDLCStoreTaskID dlcsTaskID );

	void TickRefreshCatalog( CryDLCStoreTaskID dlcsTaskID );
	void TickGetOrderTotal( CryDLCStoreTaskID dlcsTaskID );

	bool EnsureD2GInitialised( CryDLCStoreTaskID dlcsTaskID );

	virtual void StartTaskRunning( CryDLCStoreTaskID dlcsTaskID );
	virtual void StopTaskRunning( CryDLCStoreTaskID dlcsTaskID );
	virtual void EndTask( CryDLCStoreTaskID dlcsTaskID );
	virtual void FreeTask( CryDLCStoreTaskID dlcsTaskID );
	virtual void ClearTask( CryDLCStoreTaskID dlcsTaskID );

	static void GetStoreAvailabilityCallback( GSResult result, D2GGetStoreAvailabilityResponse* pResponse, void* pUserData );
	static void LoadCatalogItemsCallback( GSResult result, D2GLoadCatalogItemsResponse *pResponse, void *pUserData );
	static void GetPurchaseHistoryCallback( GSResult result, D2GGetPurchaseHistoryResponse *pResponse, void *pUserData );
	static uint32 CountItemsInPurchaseHistory( D2GPurchaseHistory* pPurchaseHistory );
	static uint32 CountItemsInOrderPurchase( D2GOrderPurchase* pOrderPurchase );
	static uint32 CountItemsInOrderItemPurchase( D2GOrderItemPurchase* pOrderItemPurchase );
	void WriteItemsInPurchaseHistory(STask* pTask,  D2GPurchaseHistory* pPurchaseHistory, SPurchaseHistoryItem*& pItem );
	void WriteItemsInOrderPurchase( STask* pTask, D2GOrderPurchase* pOrderPurchase, SPurchaseHistoryItem*& pItem );
	void WriteItemsInOrderItemPurchase( STask* pTask, D2GOrderItemPurchase* pOrderItemPurchase, SPurchaseHistoryItem*& pItem );
	static void GetUserCreditCardsCallback( GSResult result, D2GGetUserCreditCardsResponse* pResponse, void* pUserData);
	static void GetOrderTotalCallback( GSResult result, D2GGetOrderTotalResponse* pResponse, void* pUserData );
	static void StartOrderCallback( GSResult result, D2GStartOrderResponse* pResponse, void* pUserData );
	static void OrderCompleteCallback( GSResult result, D2GIsOrderCompleteResponse* pResponse, void* pUserData );
	void DownloadProgressCallbackHandler( STaskLink* pLink, gsi_u32 bytesReceived, gsi_u32 bytesTotal );
	static void DownloadProgressCallback( gsi_u32 bytesReceived, gsi_u32 bytesTotal, void* pUserData );
	void DownloadCompleteCallbackHandler( STaskLink* pLink, GSResult result, string path );
	static void DownloadCompleteCallback( GSResult result, gsi_char* pSaveFile, gsi_char* url, void* pUserData );
	void DispatchDownloadComplete( SDispatchDownloadCompleteParam param );
	void DispatchDownloadProgress( CryDLCUserDownloadItemCallback cb, CryLobbyTaskID lTaskID, CryDLCDownloadableID downloadableID, uint32 bytesReceived, uint32 bytesTotal, void* pCbArg );

	virtual void FreeOrderHandle( CryDLCOrderHandle orderHandle );
	virtual void ClearOrderHandle( CryDLCOrderHandle orderHandle );

	bool CreateTaskLink( CryDLCStoreTaskID dlcsTaskID );
	void PassTaskLinkToGameSpy( CryDLCStoreTaskID dlcsTaskID );
	static void FreeTaskLinkFromCryNetwork( STask* pTask );
	static void FreeTaskLinkFromGameSpy( STaskLink* pLink );
	bool CreateOrderLink( CryDLCOrderHandle h );
	void PassOrderLinkToGameSpy( CryDLCOrderHandle h );
	static void FreeOrderLinkFromCryNetwork( SOrder* pOrder );
	static void FreeOrderLinkFromGameSpy( SOrderLink* pLink );

	void InitializeTaskGameSpyObjects( STask* pTask, uint8 newObjects, uint8 oldObjects );
	ETaskGameSpyCheckResult CheckTaskGameSpyObjects( CryMatchMakingTaskID mmTaskID );
	void FixCurrencyString( wchar_t* pRawStr, CryStringT< wchar_t >& fixedString );

	STask													m_tasks[ MAX_DLCSTORE_TASKS ];
	SOrder												m_orders[ MAX_DLCSTORE_ORDERS ];
	CWorkQueue										m_fromD2GDownloadThreadQueue;
	NetFastMutex									m_fromD2GDownloadThreadQueueMutex;
	std::vector< STaskLinkPtr >		m_taskLinks;
	std::vector< SOrderLinkPtr >	m_orderLinks;
	D2GCategoryList								m_D2GCategoryList;
	D2GInstancePtr								m_pD2GInstance;
	D2GCatalogPtr									m_pD2GCatalog;
	D2GCatalogItemList*						m_pD2GCatalogItemList;
	D2GDownloadReference*					m_pD2GDownloadRef;
};


#endif // USE_CRYLOBBY_GAMESPY


#endif // __CRYGAMESPYDLCSTORE_H__
